﻿Imports System.Runtime.CompilerServices

Module RebuildFixFile
	Dim DataFile As XDocument = XDocument.Load("C:\Source\FixSharp\Jonathans Scratch Space\FIX44.xml")
	Dim Target As String = "C:\Source\FixSharp\Jonathans Scratch Space\FIX44_New.xml"

	Sub Main()
		'Dim types = From node In DataFile...<field> Select node.@type Distinct
		'For Each t In types
		'	Console.WriteLine(t)
		'Next
		'Console.ReadLine()


		Dim targetDoc As New XDocument
		FixCase(DataFile.Root, targetDoc)

		targetDoc.Save(Target)

		Console.WriteLine("Done Converting")

		Console.WriteLine("Loading Fields")

		Dim fieldDataTypes As New Dictionary(Of String, String)
		For Each item In (From node In targetDoc.<Fix>.<Fields>.<Field> Select node.@Name, node.@Type)
			Console.Write(".")
			fieldDataTypes.Add(item.Name, item.Type)
		Next

		Console.WriteLine()
		Console.WriteLine("Checking enumerations")
		For Each item In (From node In targetDoc.<Fix>.<Fields>.<Field> Where node...<Enum>.Count > 0 Select node)
			Select Case item.@Type
				Case "String", "Char", "Int", "MultipleValueString"
					Console.WriteLine()
				Case Else
					Console.WriteLine(item.@Name & " has enumerations but is a " & item.@Type)
			End Select
		Next


		Console.WriteLine()
		Console.WriteLine("Checking groups")

		For Each item In (From node In targetDoc...<Group> Select node)
			Console.Write(".")
			If fieldDataTypes(item.@Name) <> "NumInGroup" Then
				Console.WriteLine(vbCrLf & "Field type error in " & item.@Name & " of " & item.Parent.@Name)
				Console.WriteLine("Found type " & fieldDataTypes(item.@Name))
			End If
		Next
		Console.WriteLine()

		Console.WriteLine("Checking data sections")

		Dim dataTypes = From a In fieldDataTypes.Keys Where fieldDataTypes(a) = "Data" Select a
		For Each item In (From node In targetDoc...<Field> Where node.Parent.Name.LocalName <> "Fields" AndAlso fieldDataTypes(node.@Name) = "Data")
			Console.Write(".")
			If fieldDataTypes(CType(item.PreviousNode, XElement).@Name) <> "Length" Then
				Console.WriteLine(vbCrLf & "Field: " & item.@Name & vbTab & "follows" & CType(item.PreviousNode, XElement).@Name & vbTab & fieldDataTypes(CType(item.PreviousNode, XElement).@Name))
			End If
		Next



		Console.WriteLine()

		Console.ReadLine()
	End Sub


	Sub FixCase(ByVal node As XElement, ByVal newParent As XContainer)
		Dim oldName As String = node.Name.LocalName

		'special cases
		If oldName = "field" AndAlso node.@number = "552" Then
			newParent.Add(<Field EnumerationMode="None" Number="552" Name="NoSides" Type="NumInGroup" RangeMax="2"/>)
			Return
		End If

		Dim newName As String
		Select Case oldName
			Case "value"
				newName = "Enum"

				If TypeOf newParent Is XElement Then 'mark the parent as an enumerable field
					CType(newParent, XElement).Attribute("EnumerationMode").Value = "Lenient"
				End If

			Case Else
				newName = oldName.CapFirst
		End Select

		Dim newNode As New XElement(newName)

		If newName = "Field" Then
			newNode.Add(New XAttribute("EnumerationMode", "None"))
		End If

		newParent.Add(newNode)
		For Each a In node.Attributes

			Dim attName As String = a.Name.LocalName.CapFirst
			Dim attValue As String = a.Value

			Select Case a.Name.LocalName
				Case "number"
					attName = "TagNumber"

				Case "required"
					Select Case attValue
						Case "Y" : attValue = "True"
						Case "N" : attValue = "False"
					End Select

				Case "description"
					attName = "Name"
					attValue = String.Join("", attValue.Split("_").Select(Function(word) word.CapFirst).ToArray)

				Case "enum"
					attName = "FixValue"

				Case "type"
					'special handling
					Select Case a.Value
						Case "STRING" : attValue = "String"
						Case "CHAR" : attValue = "Char"
						Case "PRICE" : attValue = "Price"
						Case "SEQNUM" : attValue = "SeqNum"
						Case "LENGTH" : attValue = "Length"
						Case "AMT" : attValue = "Amt"
						Case "QTY" : attValue = "Qty"
						Case "CURRENCY" : attValue = "Currency"
						Case "MULTIPLEVALUESTRING" : attValue = "MultipleValueString"
						Case "EXCHANGE" : attValue = "Exchange"
						Case "NUMINGROUP" : attValue = "NumInGroup"
						Case "UTCTIMESTAMP" : attValue = "UtcTimestamp"
						Case "BOOLEAN" : attValue = "Boolean"
						Case "LOCALMKTDATE" : attValue = "LocalMktDate"
						Case "INT" : attValue = "Int"
						Case "DATA" : attValue = "Data"
						Case "FLOAT" : attValue = "Float"
						Case "PERCENTAGE" : attValue = "Percentage"
						Case "PRICEOFFSET" : attValue = "PriceOffset"
						Case "MONTHYEAR" : attValue = "MonthYear"
						Case "UTCDATEONLY" : attValue = "UtcDateOnly"
						Case "UTCTIMEONLY" : attValue = "UtcTimeOnly"
						Case "COUNTRY" : attValue = "Country"
						Case Else
							attValue = a.Value
					End Select
					attName = "Type"
			End Select
			newNode.Add(New XAttribute(attName, attValue))

		Next
		For Each childNode In node.Elements
			FixCase(childNode, newNode)
		Next

	End Sub


	<Extension()> Function CapFirst(ByVal value As String) As String

		'This addresses words in all caps
		If value = value.ToUpper Then value = value.ToLower

		Return value.Substring(0, 1).ToUpper & value.Substring(1)
	End Function

End Module
